import { message, Tree } from 'antd';
import React, { useEffect, useState } from 'react';
import styles from './index.module.sass';

export interface TreeData {
    /**树节点标题 */
    title: string

    /**树节点的值 */
    key: string

    /**节点的唯一值 */
    id: string

    /**父节点的唯一值 */
    parentId: string

    /**当前树节点的子节点 */
    children?: TreeData[]
}

interface Props {
    /**远程请求函数 */
    remote: Function

    /**默认选择的树节点 */
    defaultSelected?: string[]

    /**树形控件的半选节点 */
    halfList?: string[]

    /**选中项目的自定义处理函数 */
    onCheck?: Function

    /**用于触发表单值的更新 */
    onChange?: Function

    /**表单设置的控件默认值 */
    value?: any

    /**最大高度，默认200 */
    maxHeight?: number

    /**节点选择是否完全受控 */
    checkStrictly?: boolean

    /**禁用远程树控件 */
    isDisabled?: boolean
}

/**
 * 获取选中节点下的所有子节点ID数组
 * @param childrenNode 
 * @param idList 
 * @returns 选中节点下所有子节点ID组成的数组
 */
function getChildrenIdList(childrenNode: TreeData[]) {
    const childrenIdList: any = [];
    (function main(childrenNode: TreeData[]) {
        childrenNode.forEach(({ id, children }) => {
            children && main(children);
            childrenIdList.push(id);
        })
    })(childrenNode)
    return childrenIdList;
}

/**
 * 根据选中节点获取父节点信息
 * @param id 选中节点ID
 * @param treeData 树形数据 
 * @returns 父节点ID为键、子节点ID组成的数组为值的对象
 */
function getParentInfo(id: any, treeData: TreeData[]) {
    let parentIdList: any;
    (function main(id: any, treeNode: TreeData[], parentInfo: any) {
        const findNode = treeNode.find((item: TreeData) => item.id === id);
        if (findNode) {
            parentIdList = parentInfo;
        } else {
            treeNode.forEach((item: TreeData) => {
                const nodeInfo: any = {};
                if (item.children) {
                    nodeInfo[item.id] = item.children.map(({ id }: any) => id);
                    main(id, item.children, { ...parentInfo, ...nodeInfo });
                }
            })
        }
    })(id, _.cloneDeep(treeData), {});
    return parentIdList;
}

/**
 * 判断父节点是全选还是半选状态
 * @param treeData 树形控件数据源
 * @param parentIdList 需要判断的父节点数据对象：key为父节点值，value为子节点组成的数组
 * @param checkList 全选节点数组
 * @param halfCheckList 半选节点数组
 */
function setParentStatus(
    treeData: TreeData[],
    parentIdList: any,
    checkList: string[],
    halfCheckList: string[]
) {
    (function main(_treeData: TreeData[], _parentIdList: any) {
        _treeData.forEach(({ id, children }) => {
            if (!_.isEmpty(_parentIdList[id])) {
                children && main(children, _parentIdList);
                const isChecked = _parentIdList[id].every((parendId: any) => {

                    return checkList.map((checkId: any) => checkId).indexOf(parendId) > -1;
                })
                if (isChecked) {
                    checkList.push(id);
                } else {
                    halfCheckList.push(id);
                }
            }
        })
    })(treeData, parentIdList)
}

/**
 * 判断选中节点的父节点及子节点状态
 * @returns 全选节点数组、半选节点数组所组成的数组
 */
function checkTreeIsHalf(checkedKeys: any, evt: any, treeData: TreeData[], checkStrictly: boolean) {
    // evt-checked：选中节点的当前状态
    const { node: { checked, children, id } } = evt;
    const childrenIdList = getChildrenIdList(children);
    const parentIdList = getParentInfo(id, treeData);

    let { checked: checkList, halfChecked: halfCheckList } = _.cloneDeep(checkedKeys);
    if (checked) {
        checkList = checkList.filter((key: any) => childrenIdList.indexOf(key) === -1);
        halfCheckList = halfCheckList.filter((key: any) => childrenIdList.indexOf(key) === -1);
        _.forInRight(parentIdList, (value: any, key: any) => {
            key = _.isNumber(+key) ? +key : key;
            _.remove(checkList, (item: any) => item === key);
            if (checkStrictly) {
                halfCheckList.push(key);
            } else {
                _.remove(halfCheckList, (item: any) => item === key);
                const hasChecked = value.some((key: any) => checkList.indexOf(key) > -1);
                const hasHalfChecked = value.some((key: any) => halfCheckList.indexOf(key) > -1);
                if (hasChecked || hasHalfChecked) {
                    halfCheckList.push(key);
                }
            }
        })
    } else {
        checkList = _.uniq(checkList.concat(childrenIdList));
        setParentStatus(treeData, parentIdList, checkList, halfCheckList);
    }
    return [_.uniq(checkList), _.uniq(halfCheckList)]
}

const RemoteTree: React.FC<Props> = ({
    remote,
    defaultSelected,
    halfList,
    onCheck,
    onChange,
    value,
    maxHeight,
    checkStrictly = true,
    isDisabled
}) => {
    console.debug('RemoteTree', defaultSelected, halfList);

    const [treeData, setTreeData] = useState<TreeData[]>([]);
    const [checkedKeys, setCheckedKeys] = useState<any>([]);

    useEffect(() => {
        async function main() {
            try {
                const [_treeData, _checkedKeys] = await remote();
                if (_checkedKeys) {
                    setCheckedKeys(_checkedKeys);
                }
                setTreeData(_treeData);
            } catch (error) {
                console.error(error);
                message.error('树形控件数据加载失败');
            }
        }
        main();
    }, [remote])

    useEffect(() => {
        setCheckedKeys({
            checked: defaultSelected,
            halfChecked: halfList || [],
        })
    }, [defaultSelected, halfList])

    const handleCheck = (checkedKeys: any, evt: any) => {
        const [checkList, halfCheckList] = checkTreeIsHalf(checkedKeys, evt, treeData, checkStrictly);

        const selectKeys = { checked: checkList, halfChecked: halfCheckList };
        setCheckedKeys(selectKeys);
        onCheck && onCheck(selectKeys, evt);
        onChange && onChange([...checkList, ...halfCheckList]);
    };

    return (
        <article className={styles.wrapper} style={{ maxHeight: maxHeight || 200 }}>
            <Tree
                checkStrictly
                checkable
                selectable={false}
                showLine={true}
                checkedKeys={checkedKeys}
                treeData={treeData}
                onCheck={handleCheck}
                disabled={isDisabled}
            />
        </article>
    )
}

export default RemoteTree;
